﻿#region Copyright & License
//
// Project: Quark Framework
// Project site: http://quark.codeplex.com
// See the copyright statement on the project site for more details.
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Threading;
using System.Xml.Serialization;
using Quark.Framework.Threading;

namespace Quark.Profiler.Framework
{
    /// <summary>
    /// 分析测试项目扩展类
    /// </summary>
    public partial class ProfilingTestProject
    {
        #region Members

        private object _syncRoot;
        private long _state;
        private IProfilingContext _profilerContext;
        private long _totalTestCasesCount;
        private long _startedTestCasesCount;
        private long _finishedTestCasesCount;

        private string _sourceFilePath;

        #endregion

        #region Constructors

        /// <summary/>
        public ProfilingTestProject(IProfilingContext context)
        {
            this._profilerContext = context;
            this.State = ProfilingTestState.Ready;
            this.Id = Guid.NewGuid().ToString();
            this.Title = ResProfilerFramework.ProfilingTestProjectDefaultTitle;
            this.Description = string.Empty;
            this.Cases = new ProfilingTestCase[] { };

            this._syncRoot = new object();
            this._totalTestCasesCount = 0;
            this._startedTestCasesCount = 0;
            this._finishedTestCasesCount = 0;

            this._sourceFilePath = string.Empty;
        }

        #endregion

        #region Load/Save

        public bool HasSourceFilePath
        {
            get { return ((this._sourceFilePath != null) && (this._sourceFilePath.Length > 0)); }
        }

        public string SourceFilePath
        {
            get { return this._sourceFilePath; }
        }

        /// <summary>
        /// 载入指定路径的测试项目
        /// </summary>
        /// <param name="projectFilePath">测试项目文件路径</param>
        /// <returns>测试项目</returns>
        public static ProfilingTestProject LoadProject(string projectFilePath)
        {
            using (FileStream fs = new FileStream(projectFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                XmlSerializer xs = new XmlSerializer(typeof(ProfilingTestProject));
                ProfilingTestProject project = (ProfilingTestProject)xs.Deserialize(fs);
                project._sourceFilePath = projectFilePath;

                return project;
            }
        }

        /// <summary>
        /// 保存当前测试项目到指定路径
        /// </summary>
        /// <param name="projectFilePath">测试项目的文件路径</param>
        public void SaveProject(string projectFilePath)
        {
            using (FileStream fs = new FileStream(projectFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
            {
                XmlSerializer xs = new XmlSerializer(typeof(ProfilingTestProject));
                xs.Serialize(fs, this);

                this._sourceFilePath = projectFilePath;
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// 获取或设置项目当前状态
        /// </summary>
        [XmlIgnore()]
        public ProfilingTestState State
        {
            get
            {
                return (ProfilingTestState)Interlocked.Read(ref _state);
            }
            private set
            {
                Interlocked.Exchange(ref _state, (long)value);
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// 当前项目被启动之前触发的事件
        /// </summary>
        public event ProfilingTestProjectStartingHandler OnTestProjectStarting;
        /// <summary>
        /// 当前项目被启动之后触发的事件
        /// </summary>
        public event ProfilingTestProjectStartedHandler OnTestProjectStarted;
        /// <summary>
        /// 当前项目运行完毕之后触发的事件
        /// </summary>
        public event ProfilingTestProjectFinishedHandler OnTestProjectFinished;
        /// <summary>
        /// 当前项目被停止运行之前触发的事件
        /// </summary>
        public event ProfilingTestProjectStoppingHandler OnTestProjectStopping;

        #endregion

        #region TestCase Event Handlers

        private void testCase_OnTestCaseFinishedInternal(object sender, ProfilingTestCaseFinishedEventArgs e)
        {
            long count = Interlocked.Increment(ref _finishedTestCasesCount);

            if (count == Interlocked.Read(ref _totalTestCasesCount))
            {
                this.AfterProjectAllCasesFinished(e.IsStopped);
            }
        }

        private void testCase_OnTestCaseStartingInternal(object sender, ProfilingTestCaseEventArgs e)
        {
        }

        private void testCase_OnTestCaseStartedInternal(object sender, ProfilingTestCaseEventArgs e)
        {
            if (this.State == ProfilingTestState.Starting)
            {
                long count = Interlocked.Increment(ref _startedTestCasesCount);
                if (count == Interlocked.Read(ref _totalTestCasesCount))
                {
                    this.AfterProjectAllCasesStarted();
                }
            }
        }

        #endregion

        #region TestCase Management

        /// <summary>
        /// 创建并返回一个新的测试用例实例
        /// </summary>
        /// <param name="title">测试用例标题</param>
        /// <param name="description">测试用例描述</param>
        /// <param name="loopTimes">测试用例的测试循环次数</param>
        /// <param name="loopForever">是否无限循环</param>
        /// <returns>新的测试用例实例</returns>
        private ProfilingTestCase CreateNewTestCase(string title, string description)
        {
            ProfilingTestCase testCase = new ProfilingTestCase(this, this._profilerContext);
            testCase.Title = title == null ? ResProfilerFramework.ProfilingTestCaseDefaultTitle : title;
            testCase.Description = description == null ? string.Empty : description;

            testCase.OnTestCaseStartingInternal += new ProfilingTestCaseStartingHandler(testCase_OnTestCaseStartingInternal);
            testCase.OnTestCaseStartedInternal += new ProfilingTestCaseStartedHandler(testCase_OnTestCaseStartedInternal);
            testCase.OnTestCaseFinishedInternal += new ProfilingTestCaseFinishedHandler(testCase_OnTestCaseFinishedInternal);

            return testCase;
        }

        /// <summary>
        /// 获取当前集合中用例的总数
        /// </summary>
        public int TestCaseCount
        {
            get
            {
                return (this.Cases == null ? 0 : this.Cases.Length);
            }
        }

        /// <summary>
        /// 创建并返回一个新的测试用例实例，该新实例被自动添加到当前项目用例集合的末尾
        /// </summary>
        /// <param name="title">测试用例标题</param>
        /// <param name="description">测试用例描述</param>
        /// <param name="loopTimes">测试用例的测试循环次数，如果等于<see cref="ProfilingTestCase.LOOP_FOREVER_TIMES"/>则表示无限循环</param>
        /// <returns>新的测试用例实例</returns>
        public ProfilingTestCase AppendNewTestCase(string title, string description)
        {
            ProfilingTestCase testCase = this.CreateNewTestCase(title, description);
            this.Cases = ProfilingArrayUtil.AddElement<ProfilingTestCase>(this.Cases, testCase);

            return testCase;
        }

        /// <summary>
        /// 删除指定的测试用例
        /// </summary>
        /// <param name="testCase">测试用例</param>
        public void RemoveTestCase(ProfilingTestCase testCase)
        {
            this.Cases = ProfilingArrayUtil.RemoveElement<ProfilingTestCase>(this.Cases, testCase);
        }

        /// <summary>
        /// 删除所有测试用例
        /// </summary>
        public void ClearTestCases()
        {
            this.Cases = new ProfilingTestCase[] { };
        }

        /// <summary>
        /// 将指定的测试用例在用例集合中上移一位
        /// </summary>
        /// <param name="testCase">测试用例</param>
        public void MoveTestCaseUp(ProfilingTestCase testCase)
        {
            this.Cases = ProfilingArrayUtil.MoveElementUp<ProfilingTestCase>(this.Cases, testCase);
        }

        /// <summary>
        /// 将指定的测试用例在用例集合中下移一位
        /// </summary>
        /// <param name="testCase">测试用例</param>
        public void MoveTestCaseDown(ProfilingTestCase testCase)
        {
            this.Cases = ProfilingArrayUtil.MoveElementDown<ProfilingTestCase>(this.Cases, testCase);
        }

        #endregion

        #region Project Management

        private void AfterProjectAllCasesStarted()
        {
            this.State = ProfilingTestState.Started;

            if (this.OnTestProjectStarted != null)
            {
                this.OnTestProjectStarted(this, new ProfilingTestProjectEventArgs(this._profilerContext));
            }
        }

        private void AfterProjectAllCasesFinished(bool isStopped)
        {
            this.State = ProfilingTestState.Finished;

            if (this.OnTestProjectFinished != null)
            {
                this.OnTestProjectFinished(this, new ProfilingTestProjectFinishedEventArgs(this._profilerContext, isStopped));
            }
        }

        /// <summary>
        /// 运行测试项目
        /// </summary>
        public void StartTest()
        {
            lock (this._syncRoot)
            {
                if ((this.Cases == null) || (this.Cases.Length == 0)) throw new NullReferenceException();
                if ((this.State != ProfilingTestState.Ready) && (this.State != ProfilingTestState.Finished))
                    throw new InvalidOperationException();

                if (this.OnTestProjectStarting != null)
                {
                    this.OnTestProjectStarting(this, new ProfilingTestProjectEventArgs(this._profilerContext));
                }

                this.State = ProfilingTestState.Starting;
                this._totalTestCasesCount = this.Cases.Length;
                this._startedTestCasesCount = 0;
                this._finishedTestCasesCount = 0;

                foreach (ProfilingTestCase testCase in this.Cases)
                {
                    testCase.StartTestCase();
                }
            }
        }

        /// <summary>
        /// 停止运行测试项目
        /// </summary>
        public void StopTest()
        {
            lock (this._syncRoot)
            {
                if (this.State != ProfilingTestState.Started) throw new InvalidOperationException();

                if (this.OnTestProjectStopping != null)
                {
                    this.OnTestProjectStopping(this, new ProfilingTestProjectEventArgs(this._profilerContext));
                }

                this.State = ProfilingTestState.Stopping;
                this._finishedTestCasesCount = 0;

                foreach (ProfilingTestCase testCase in this.Cases)
                {
                    testCase.StopTestCase();
                }
            }
        }

        #endregion
    }

    /// <summary>
    /// 测试项目状态枚举
    /// </summary>
    public enum ProfilingTestState : long
    {
        Ready = 0,
        Starting,
        Started,
        Stopping,
        Finished
    }
}
